import socket
import struct
import time
import threading
import os
import func_timeout
from func_timeout import func_set_timeout

BUF_SIZE = 1038
CLIENT_PORT = 7777
FILE_SIZE = 1024
RECV_BUF_SIZE = 1038 * 64
SEND_BUF_SIZE = 1038 * 64
SERVER_IP = "localhost"
SERVER_PORT = 7776
SERVER_ADDRESS = (SERVER_IP,SERVER_PORT)
FILE_NAME = '123.jpg'
CLIENT_ADDR = 'localhost'

feedback_struct = struct.Struct('II')
packet_struct = struct.Struct('!IIIbb1024s')

lock = threading.Lock()
speed = 0

seq = 0
ack = 0
file_size =  os.stat(FILE_NAME).st_size
sended_bytes = 0

def get_packet(timestamp, file, client_socket):
    global seq,ack,speed,sended_bytes

    while True:
        lock.acquire()
        file.seek(seq*1024)
        data = file.read(1024)
        if not data:
            print('共发送'+str(seq)+'个数据包，文件总大小为'+str(os.stat(FILE_NAME).st_size))
            lock.release()
            break 
        packet_size = 4 + 4 + 1 + 1 + 1 + 1 + 1024  
        end_flag = 0 if len(data) == 1024 else 1
        header_size = 12 
        packet = packet_struct.pack(ack, seq, packet_size,  header_size, end_flag, data)
        seq += 1
        ack += 1
        lock.release()
        
        send_packet(client_socket, packet,seq)
        sended_bytes = len(data)*(seq+1)
        speed = (sended_bytes/(time.time()-timestamp))/1000

def send_packet(client_socket, packet, seq):
    client_socket.sendto(packet,SERVER_ADDRESS)
    stop_event = threading.Event()
    resend_thread = threading.Thread(target=resend_packet, args=(client_socket,packet,seq,stop_event))
    resend_thread.start()
    timer = threading.Timer(5, stop_event.set)
    timer.start()

    
def resend_packet(client_socket, packet,seq,stop_event):
    while not stop_event.is_set():
        feedback_pack, _ = client_socket.recvfrom(BUF_SIZE)
        ack  = feedback_struct.unpack(feedback_pack)[0]
        if ack == seq:
            break
        else:
            break
    
def connect(client_socket):
    get_connect_data = "Sender require connection"
    client_socket.sendto(get_connect_data.encode('utf-8'),SERVER_ADDRESS)
    connect_recv_data, server_address = client_socket.recvfrom(BUF_SIZE)
    print(connect_recv_data.decode('utf-8')+' from '+str(server_address[0])+":"+str(server_address[1]))
    confirm_connect_data = "Sender confirm connection"+FILE_NAME.split('/')[-1]
    client_socket.sendto(confirm_connect_data.encode('utf-8'),server_address)
    
def main():
    
    client_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    client_socket.setsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF,SEND_BUF_SIZE)
    client_socket.setsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF,RECV_BUF_SIZE)
    client_socket.bind((CLIENT_ADDR,7777))
    connect(client_socket)
    threads = []
    timestamp = time.time()
    with open(FILE_NAME, 'rb') as file:
        for i in range(4):
            thread = threading.Thread(target=get_packet, args=(timestamp, file, client_socket))
            thread.start()
            threads.append(thread)
        
        for thread in threads:
            thread.join()
    print('\n开始中断连接')
   # 中断连接，四次挥手
    data = 'Client requests disconnection'
    print(data)
    client_socket.sendto(data.encode('utf-8'),SERVER_ADDRESS)
    data,client_addr = client_socket.recvfrom(BUF_SIZE)
    print(data.decode('utf-8'))
    data,client_addr = client_socket.recvfrom(BUF_SIZE)
    print(data.decode('utf-8'))
    data = 'Client allows disconnection'
    client_socket.sendto(data.encode('utf-8'),SERVER_ADDRESS)
    print(data)
    print('The connection between client and server has been interrupted')
    client_socket.close()
    
if __name__ == "__main__":
    main()